#include "ZoneManager.h"
#include "../../Render/TextureManager.h"

ZoneManager ZoneManager::m_Instance;

ZoneManager::ZoneManager(void)
{
	m_WorldZones.RemoveAll();
	m_WorldHeight = 0;
	m_WorldWidth = 0;
	m_Random.SeedRandom();
	m_ZoneChance[ZONE_MOUNTAIN] = 0.05f;
	m_ZoneChance[ZONE_PLAINS] = 0.35f;
	m_ZoneChance[ZONE_WATER] = 0.10f;
	m_ZoneChance[ZONE_FOREST] = 0.30f;
	m_ZoneChance[ZONE_DESERT] = 0.20f;
}

ZoneManager::~ZoneManager(void)
{
}

ZoneManager* ZoneManager::GetInstance(void)
{
	return &m_Instance;
}

void ZoneManager::Initialize(void)
{
	m_WorldZones.RemoveAll();
	m_WorldHeight = 0;
	m_WorldWidth = 0;
	m_ZoneImageID = TextureManager::GetInstance()->LoadTeture("../Resource/Zones.png", 0);
	m_ZoneWidth = TextureManager::GetInstance()->GetTextureWidth(m_ZoneImageID) / ZONE_MAX;
	m_ZoneHeight = TextureManager::GetInstance()->GetTextureHeight(m_ZoneImageID);
	m_Random.SeedRandom();
}

void ZoneManager::CreateWorld(void)
{
	int worldSize = m_WorldHeight*m_WorldWidth;

	if( worldSize <= 0 )
		return;

	Zone* newZone = NULL;

	int zoneX = 0, zoneY = 0;
	for(int iter = 0; iter < worldSize; iter++)
	{
		newZone = new Zone();
		newZone->SetZoneID(iter);
		newZone->SetZoneType(ZONE_NULL);
		zoneX = iter%m_WorldWidth;
		zoneY = (iter-zoneX)/m_WorldWidth;
		newZone->SetZoneX(zoneX*m_ZoneWidth);
		newZone->SetZoneY(zoneY*m_ZoneHeight);

		m_WorldZones.AddBack(newZone);
		newZone = NULL;
	}

	for(int iter = 0; iter < worldSize; iter++)
	{
		if( !(iter%m_WorldWidth) )
		{
			m_WorldZones[iter]->SetNeighbor(NULL, WEST);
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter+1], EAST);
		}
		else if( iter%m_WorldWidth == m_WorldWidth-1 )
		{
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter-1], WEST);
			m_WorldZones[iter]->SetNeighbor(NULL, EAST);
		}
		else
		{
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter-1], WEST);
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter+1], EAST);
		}

		if( iter >= 0 && iter <= m_WorldWidth-1 )
		{
			m_WorldZones[iter]->SetNeighbor(NULL, NORTH);
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter+m_WorldWidth], SOUTH);
		}
		else if( (worldSize-1)-iter >= 0 && (worldSize-1)-iter <= m_WorldWidth-1 )
		{
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter-m_WorldWidth], NORTH);
			m_WorldZones[iter]->SetNeighbor(NULL, SOUTH);
		}
		else
		{
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter-m_WorldWidth], NORTH);
			m_WorldZones[iter]->SetNeighbor(m_WorldZones[iter+m_WorldWidth], SOUTH);
		}
	}

	m_Random.SeedRandom(m_Random.Random(INT_MIN,INT_MAX));
	for(unsigned int iter = 0; iter < (unsigned)worldSize; iter++)
	{
		Zone *pZone = GetZone(iter);
		RandomizeZone(pZone);
	}
}

void ZoneManager::RandomizeZone(Zone* zone)
{
	unsigned int neighbor = 0, rv = 0, per = 0, maxValue = (m_WorldHeight*m_WorldWidth);
	Zone* zNeighbor = zone->GetZoneNeighbor(m_Random.Random(1,4));

	// Make sure the neighbor isn't NULL
	while( !zNeighbor )
	{
		zNeighbor = zone->GetZoneNeighbor(m_Random.Random(1,4));
	}

	// Get its type
	neighbor = zNeighbor->GetZoneType();
	// Generate a random value
	rv = m_Random.Random(0, maxValue);
	per = (unsigned int)((float)maxValue * m_ZoneChance[neighbor]);

	if( rv < per )
	{
		zone->SetZoneType(neighbor);
	}
	else
	{
		unsigned int otherGuy = neighbor;
		while( otherGuy == neighbor )
		{
			otherGuy = m_Random.Random(0, ZONE_MAX);
		}
		zone->SetZoneType(otherGuy);
	}
}

void ZoneManager::Render(Camera2D& renderTarget)
{
	RECT targetZone;
	
	Zone* zoneTemp = NULL;
	int zoneType = 0, x = 0, y = 0, midX = 0, midY = 0;
	midX = m_ZoneWidth/2;
	midY = m_ZoneHeight/2;
	
	for(unsigned int iter = 0; iter < m_WorldZones.Size(); iter++)
	{
		zoneTemp = m_WorldZones[iter];
		zoneType = zoneTemp->GetZoneType();
		x = zoneTemp->GetZoneX();
		y = zoneTemp->GetZoneY();

		targetZone.left = (zoneType*m_ZoneWidth);
		targetZone.right = (zoneType*m_ZoneWidth)+m_ZoneWidth;
		targetZone.top = 0;
		targetZone.bottom = m_ZoneHeight;

		renderTarget.Draw(m_ZoneImageID, x-midX, y-midY, 1.0f, 1.0f, &targetZone, (float)(midX), (float)(midY), 0);
	}
}

void ZoneManager::Shutdown(void)
{
	Zone* zoneTemp;
	for(unsigned int iter = 0; iter < m_WorldZones.Size(); iter++)
	{
		zoneTemp = m_WorldZones[iter];
		delete zoneTemp;
	}
	m_WorldZones.RemoveAll();

	TextureManager::GetInstance()->UnloadTexture(m_ZoneImageID);
}

// Mutators
void ZoneManager::SetWorldSize(int width, int height)
{
	m_WorldWidth = width;
	m_WorldHeight = height;
}

void ZoneManager::SetZoneType(int position, int type)
{
	m_WorldZones[position]->SetZoneType(type);
}

// Accessors
Zone* ZoneManager::GetZone(int index)
{
	if( index < 0 || ((unsigned int)index) >= m_WorldZones.Size() )
		return NULL;

	return m_WorldZones[index];
}

unsigned int ZoneManager::GetWorldSize(void)
{
	unsigned int width, height;
	width = m_WorldWidth;
	height = m_WorldHeight;

	return (width*height);
}

unsigned int ZoneManager::GetWorldWidth(void)
{
	return m_WorldWidth;
}

unsigned int ZoneManager::GetWorldHeight(void)
{
	return m_WorldHeight;	
}

// Pixel values
unsigned int ZoneManager::GetWorldPWidth(void)
{
	return m_WorldWidth*m_ZoneWidth;
}

unsigned int ZoneManager::GetWorldPHeight(void)
{
	return m_WorldHeight*m_ZoneHeight;
}

void ZoneManager::SetWaterLimit()
{
	m_WaterLimit = (m_WorldHeight / 3) * (m_WorldWidth / 3);
}

void ZoneManager::AddToWaterLimit()
{
	m_WaterLimit--;
}

unsigned int ZoneManager::GetWaterLimit()
{
	return m_WaterLimit;
}